package ar.com.jsejtman.cajugastos2.core.validator.service;

import ar.com.jsejtman.cajugastos2.core.validator.entity.IValidationResultsContainer;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;

/**
 * Servicio de validación de objetos de la aplicación.
 * 
 * @author Sebastián Matienzo
 */
@Service
public class ValidatorService implements IValidatorService {

	/**
	 * Fuente de mensajes localizados de Spring.
	 */
	@Autowired
	private MessageSource messageSource;

	/**
	 * Valida un objeto usando un validador de Spring, y volcando los resultados en un contenedor de
	 * errores personalizado.
	 * 
	 * @param validator Validator de Spring a utilizar.
	 * @param objectToValidate Objeto a validar.
	 * @param customContainer Contenedor de errores personalizado para resultados de validación.
	 */
	public final void validate(final Validator validator, final Object objectToValidate,
	        final IValidationResultsContainer customContainer) {

		final Errors springErrorsContainer = new BeanPropertyBindingResult(objectToValidate, "");

		validator.validate(objectToValidate, springErrorsContainer);

		/* Obtiene el Locale a partir del cual interpretar los mensajes de error */
		final Locale locale = LocaleContextHolder.getLocale();

		if (springErrorsContainer.hasGlobalErrors()) {
			for (ObjectError globalError : springErrorsContainer.getGlobalErrors()) {

				for (String errorCode : globalError.getCodes()) {
					final String message =
					        this.getLocalizedMessage(errorCode, locale, globalError.getArguments());

					if (message != null) {
						customContainer.addGeneralValidationMessage(message);
					}
				}
			}
		}

		if (springErrorsContainer.hasFieldErrors()) {
			for (FieldError fieldError : springErrorsContainer.getFieldErrors()) {
				final String fieldName = fieldError.getField();

				for (String errorCode : fieldError.getCodes()) {

					final String message =
					        this.getLocalizedMessage(errorCode, locale, fieldError.getArguments());

					if (message != null) {
						customContainer.addFieldValidationMessage(fieldName, message);
					}
				}
			}
		}
	}

	/**
	 * Valida un objeto usando un contenedor de errores de Spring.
	 * 
	 * @param validator Validator de Spring a utilizar.
	 * @param objectToValidate Objeto a validar.
	 * @param springErrorsContainer Contenedor de errores de Spring para resultados de validación.
	 */
	public final void validate(final Validator validator, final Object objectToValidate,
	        final BindingResult springErrorsContainer) {

		validator.validate(objectToValidate, springErrorsContainer);
	}

	/**
	 * Obtiene un mensaje internacionalizado.
	 * 
	 * @param key Clave del mensaje.
	 * @param locale Localización a utilizar para el mensaje.
	 * @param arguments Argumentos del mensaje de error.
	 * @return Mensaje internacionalizado.
	 */
	private String getLocalizedMessage(final String key, final Locale locale, 
			final Object[] arguments) {

		try {
			return (this.messageSource.getMessage(key, arguments, locale));
		} catch (final Exception e) {
			return (null);
		}
	}
}
